package tacos.web;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import tacos.Taco;
import tacos.TacoOrder;
import tacos.User;
import tacos.data.OrderRepository;
import tacos.data.UserRepository;

import javax.validation.Valid;
import java.security.Principal;

@Slf4j
@Controller
@RequestMapping("/orders")
@SessionAttributes("tacoOrder")
public class OrderController {

    private OrderRepository orderRepo;
    private UserRepository userRepo;

    @Autowired
    public OrderController(OrderRepository orderRepo, UserRepository userRepo) {
        this.orderRepo = orderRepo;
        this.userRepo = userRepo;
    }

    @GetMapping("/current")
    public String orderForm(@ModelAttribute TacoOrder tacoOrder, Model model) {
        model.addAttribute("tacoOrder", tacoOrder);
        return "orderForm";
    }

    /*
    * 有多种方式确定用户是谁，常用的方式如下：
    * ●将java.security.Principal对象注入控制器方法；
    *   这种方法能够正常运行，但是它在与安全无关的功能中掺杂了与安全有关的代码。我们可以修改processOrder()方法，让它不再接收Principal参数，
    *   转而接收Authentication对象作为参数，从而消除与安全有关的代码.
    * ●将org.springframework.security.core.Authentication对象注入控制器方法；
    *   有了Authentication对象之后，我们就可以调用getPrincipal()来获取principal对象。在本例中，它是一个User对象。
    *   需要注意，getPrincipal()返回的是java.util.Object，所以我们需要将其转换成User。最整洁的方案可能是在processOrder()中直接接收一个User对象，
    *   不过，我们需要为其添加@AuthenticationPrincipal注解，使其变成authentication的principal.
    * ●使用org.springframework.security.core.context.SecurityContextHolder获取安全上下文；
    *   还有另一种方法能够识别当前认证用户，但是这种方法有点麻烦，包含大量与安全有关的代码。我们可以从安全上下文中获取一个Authentication对象，然后像下面这样获取它的principal：
    *   Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    *   User user = (User) authentication.getPrincipal();
    * ●注入@AuthenticationPrincipal注解标注的方法参数（@AuthenticationPrincipal来自Spring Security的org.springframework.security.core.annotation包）。
    * */
    @PostMapping
    public String processOrder(@Valid TacoOrder order, Errors errors, SessionStatus sessionStatus, Principal principal) {
        log.info("Order submitted: {}", order);
        /*
        * SessionStatus对象的setComplete()方法，这个SessionStatus对象是以参数的形式传递进来的。
        * 当用户创建他们的第一个taco时，TacoOrder对象会被初始创建并放到会话中。通过调用setComplete()，我们能够确保会话被清理掉，从而为用户在下次创建taco时为新的订单做好准备。
        * */

        if (errors.hasErrors()) {
            return "orderForm";
        }

        log.info("Order submitted: {}", order);
        User user = this.userRepo.findByUsername(principal.getName());
        order.setUser(user);
        this.orderRepo.save(order);
        sessionStatus.setComplete();

        return "redirect:/";
    }
}
